package com.zhentao.studyim.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * Netty WebSocket服务器
 * 提供WebSocket连接服务
 */
@Slf4j
@Component
public class NettyServer {

    @Value("${netty.server.port}")
    private int port;

    @Value("${netty.server.boss-threads}")
    private int bossThreads;

    @Value("${netty.server.worker-threads}")
    private int workerThreads;

    @Autowired
    private WebSocketHandler webSocketHandler;

    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;
    private Channel serverChannel;

    /**
     * 启动Netty服务器
     * @PostConstruct 注解表示在Bean初始化后自动调用
     */
    @PostConstruct
    public void start() {
        new Thread(() -> {
            try {
                // 1. 创建事件循环组
                bossGroup = new NioEventLoopGroup(bossThreads);      // 处理连接
                workerGroup = new NioEventLoopGroup(workerThreads);  // 处理I/O

                // 2. 创建服务器启动器
                ServerBootstrap bootstrap = new ServerBootstrap();
                bootstrap.group(bossGroup, workerGroup)
                        .channel(NioServerSocketChannel.class)           // 使用NIO
                        .option(ChannelOption.SO_BACKLOG, 128)          // 连接队列大小
                        .childOption(ChannelOption.SO_KEEPALIVE, true)  // 保持连接
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel ch) throws Exception {
                                ChannelPipeline pipeline = ch.pipeline();

                                // 3. 添加处理器链
                                // HTTP编解码器
                                pipeline.addLast(new HttpServerCodec());

                                // HTTP对象聚合器，将多个HTTP消息聚合成一个完整的HTTP消息
                                pipeline.addLast(new HttpObjectAggregator(65536));

                                // CORS跨域处理器
                                pipeline.addLast(new CorsHandler());

                                // 支持大文件传输
                                pipeline.addLast(new ChunkedWriteHandler());

                                // WebSocket协议处理器
                                pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));

                                // 自定义WebSocket消息处理器
                                pipeline.addLast(webSocketHandler);
                            }
                        });

                // 4. 绑定端口并启动服务器
                ChannelFuture future = bootstrap.bind("localhost", port).sync();
                serverChannel = future.channel();

                log.info("Netty WebSocket服务器启动成功，监听地址: localhost:{}", port);

                // 5. 等待服务器关闭
                future.channel().closeFuture().sync();

            } catch (Exception e) {
                log.error("Netty服务器启动失败", e);
            } finally {
                shutdown();
            }
        }, "netty-server").start();
    }

    /**
     * 关闭Netty服务器
     * @PreDestroy 注解表示在Bean销毁前自动调用
     */
    @PreDestroy
    public void shutdown() {
        log.info("正在关闭Netty服务器...");

        if (serverChannel != null) {
            serverChannel.close();
        }

        if (bossGroup != null) {
            bossGroup.shutdownGracefully();
        }

        if (workerGroup != null) {
            workerGroup.shutdownGracefully();
        }

        log.info("Netty服务器已关闭");
    }
}